﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// For Kinect, use Microsoft.Speech, not System.Speech
using System.Speech.Recognition.SrgsGrammar;
using System.Speech.Recognition;

namespace NaoController
{
    class NaoGrammar
    {

        List<string> baseWords;
        List<string> userAddedWords;
        RecognizerInfo rec;
        Dictionary<string, List<HmmGesture>> nameToGesture;

        // Static grammars
        Grammar yesNoGrammar;
        Grammar sequenceGrammar;

        public NaoGrammar(RecognizerInfo rec)
        {
            this.baseWords = new List<string>();
            this.userAddedWords = new List<string>();
            this.nameToGesture = new Dictionary<string, List<HmmGesture>>();

            foreach (HmmGesture gesture in Enum.GetValues(typeof(HmmGesture)))
            {
                string name = parseEnumName(gesture).ToLower();

                baseWords.Add(name);


                List<HmmGesture> list = new List<HmmGesture>();
                list.Add(gesture);

                nameToGesture.Add(name, list);
            }

            // Basic setup for the grammar
            this.rec = rec;
        }

        public void AddAction(String actionName, List<HmmGesture> gestures)
        {
            this.userAddedWords.Add(actionName);
            this.nameToGesture.Add(actionName, gestures);
        }

        public Grammar CreateNAOBaseGrammar()
        {
            Console.WriteLine("CREATE NAO BASE GRAMMAR");
            // command ::= "NAO" action
            // action ::= baseAction action | "end"
            // baseAction ::= "Walk forward" | "Walk backward" | ...

            // new user actions are added to "baseAction"

            // Note: "end" should ideally be the empty string, but I'm not sure how to add this.
            // Currently, it almost always thinks it hears "end" at the end.
            
            List<string> allActions = new List<string>(this.baseWords);
            allActions.AddRange(this.userAddedWords);

            SrgsDocument srgs = new SrgsDocument();
            srgs.Culture = rec.Culture;
            srgs.Mode = SrgsGrammarMode.Voice;

            // baseAction rule. Add all of the basic predefined actions
            SrgsRule baseActionRule = new SrgsRule("baseaction");
            SrgsOneOf baseActionOptions = new SrgsOneOf(allActions.ToArray<string>());
            baseActionRule.Add(baseActionOptions);
            srgs.Rules.Add(baseActionRule);

            // action rule. Useful for recursion
            SrgsRule actionRule = new SrgsRule("action");

            SrgsItem recursion = new SrgsItem();
            recursion.Add(new SrgsRuleRef(baseActionRule));
            recursion.Add(new SrgsRuleRef(actionRule));

            SrgsOneOf actionOptions = new SrgsOneOf(new SrgsItem[] { recursion,
                new SrgsItem("end")});

            actionRule.Add(actionOptions);
            srgs.Rules.Add(actionRule);

            // the base rule. says "nao" then does an action
            SrgsRule naoCommandRule = new SrgsRule("command");
            naoCommandRule.Add(new SrgsItem("now")); // sounds closest to "nao"
            naoCommandRule.Add(new SrgsRuleRef(actionRule));
            srgs.Rules.Add(naoCommandRule);

            srgs.Root = srgs.Rules["command"];

            Grammar g = new Grammar(srgs);
            g.Name = "base";

            return g;
        }

        public Grammar CreateNAOYesNoGrammar()
        {
            if (this.yesNoGrammar == null)
            {
                Choices yesNoChoices = new Choices(new string[] { "yes save sequence", "no don't save sequence" });

                GrammarBuilder builder = new GrammarBuilder(yesNoChoices);

                this.yesNoGrammar = new Grammar(builder);
                this.yesNoGrammar.Name = "yesno";
            }

            return this.yesNoGrammar;
        }

        public Grammar CreateNAOSequenceGrammar()
        {
            if (this.sequenceGrammar == null)
            {
                //TODO it would be nice to actually name these a new thing
                ///Choices sequenceChoices = new Choices(new string[] { "sequence one", "sequence two", "sequence three", "sequence four", "sequence five" });

                GrammarBuilder builder = new GrammarBuilder();
                GrammarBuilder dictation = new GrammarBuilder();
                dictation.AppendDictation();


                builder.Append(new Choices("The sequence name is"));
                builder.Append(new SemanticResultKey("DictationInput", dictation));

                this.sequenceGrammar = new Grammar(builder);
                this.sequenceGrammar.Name = "sequence";
            }

            return this.sequenceGrammar;
        }

        /// <summary>
        /// Parses a string into a list of gestures to perform
        /// </summary>
        /// <param name="output"></param>
        /// <returns></returns>
        public Tuple<int, List<HmmGesture>> ParseGestureOutput(String output)
        {
            List<HmmGesture> gestures = new List<HmmGesture>();
            string[] gestureNames = output.Split(' ');
            
            int numGestures = 0;
            string seenSoFar = "";

            foreach (string name in gestureNames)
            {
                // Prereq: All words in 'output' must be made up of strings in our grammar

                // if we find a word that isn't the end, add it to the things we've seen so far
                if (!"now".Equals(name, StringComparison.OrdinalIgnoreCase) && !"end".Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    seenSoFar += name;

                    if (nameToGesture.ContainsKey(seenSoFar))
                    {
                        gestures.AddRange(nameToGesture[seenSoFar.ToLower()]);
                        numGestures++;
                        seenSoFar = "";
                    }
                    else
                    {
                        seenSoFar += " ";
                    }
                }

               
            }

            return Tuple.Create<int, List<HmmGesture>>(numGestures, gestures);

        }

        public String parseEnumName(HmmGesture value)
        {
            StringBuilder builder = new StringBuilder();
            foreach (char c in value.ToString())
            {
                if (Char.IsUpper(c) && builder.Length > 0)
                {
                    builder.Append(" ");
                }
                builder.Append(c);
            }

            return builder.ToString();
        }
    }
}
